package cqu.edu.cn.controller;

import cqu.edu.cn.bean.TokenSet;
import cqu.edu.cn.repository.*;
import cqu.edu.cn.bean.Users;
import cqu.edu.cn.bean.AppRegisterInfo;
import cqu.edu.cn.bean.LoginInfo;
import cqu.edu.cn.bean.RegisterMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import java.net.HttpURLConnection;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import java.util.ArrayList;
import java.util.List;
import java.io.IOException;
import java.net.URL;
import java.util.UUID;

/**
 * SSO主登录相关接口
 * 
 * @author 杨鑫隆and高鸿成
 */

@Controller
public class SSOController {

	// Spring的自动装配原理
	@Autowired
	private UserRepository userRepository;

	/*
	 * 实现登陆功能
	 */
	@RequestMapping("/sso/login")
	public String login(LoginInfo loginParam, RedirectAttributes redirectAttributes, HttpSession session, Model model,
			HttpServletResponse resp) {
		// 事先已经在MongoDB中预先存储了一些User的信息，详情可看实验报告文档说明
		String uname = loginParam.getUsername();
		// 通过用户名查找
		List<Users> AllUser = userRepository.findByUsername(uname);
		boolean flag = true;
		for (Users users : AllUser) {
			if (users.getUsername().equals(uname)) {
				// 用户输入密码和数据库中密码不匹配
				if (!users.getPassword().equals(loginParam.getPassword())) {
					flag = false;
					break;
				}
			}
		}
		if (!flag) {
			model.addAttribute("msg", "登录失败，请重新登录!");
			model.addAttribute("redirectUrl", loginParam.getRedirectUrl());
			return "login";
		}

		// 创建令牌，令牌格式用：用户名 + UUID(防止解码) 构成
		String Token = loginParam.getUsername() + UUID.randomUUID().toString();
		// 把令牌放到全局会话中
		session.setAttribute("Token", Token);
		// 将有效的令牌放到认证中心
		TokenSet.TOKEN_SET.add(Token);
		// 未携带重定向跳转地址-默认跳转到认证中心首页
		if (loginParam.getRedirectUrl().equals("")) {
			Cookie cookie = new Cookie("Token", Token);
			resp.addCookie(cookie);
			return "index";
		}
		// 携带令牌到客户端，然后客户端再去验证令牌的正确性
		redirectAttributes.addAttribute("Token", Token);
		// 跳转到客户端请求的地址
		return "redirect:" + loginParam.getRedirectUrl();
	}

	/*
	 * 校验令牌是否合法，合法则免登录进入
	 */
	@ResponseBody
	@RequestMapping("/sso/checkToken")
	public String verify(String Token, String LogoutUri, String SessionId) {
		// 判断token是否存在认证中心中,如果存在则代表合法
		boolean isVerify = TokenSet.TOKEN_SET.contains(Token);
		if (!isVerify) {
			return "false";
		}
		// 令牌存在于认证中心中，说明用户已经登录认证过了，则记录此次登录的局部会话信息
		List<AppRegisterInfo> RegisterList = RegisterMap.REGISTER_MAP.computeIfAbsent(Token,
				k -> new ArrayList<AppRegisterInfo>());
		AppRegisterInfo appInfo = new AppRegisterInfo();
		appInfo.setLogOutUrl(LogoutUri);
		appInfo.setsessionId(SessionId);
		RegisterList.add(appInfo);
		return "true";
	}

	/*
	 * 校验用户是否已经登录认证中心（是否有全局会话）
	 */
	@RequestMapping("/sso/checkLogin")
	public String checkLogin(String redirectUrl, RedirectAttributes redirectAttributes, Model model,
			HttpServletRequest request) {
		// 从全局会话中判断是否已经登录过(判断是否有全局会话)
		Object ssoToken = request.getSession().getAttribute("Token");

		// ssoToken为空
		if (ssoToken == null) {
			// model中存放的数据可以在前端视图thymeleaf中解析
			model.addAttribute("redirectUrl", redirectUrl);
			// 跳转到统一登录页面
			return "login";
		}

		// 有Token，重定向参数拼接（将会在redirectUrl后面拼接）
		redirectAttributes.addAttribute("Token", ssoToken);
		// 重定向到目标系统
		return "redirect:" + redirectUrl;
	}

	/*
	 * 统一注销，清除缓存的Token信息
	 */
	@RequestMapping("/sso/logOut")
	public String logOut(HttpServletRequest request) throws IOException {
		// 注销全局回话
		HttpSession session = request.getSession();
		session.invalidate();
		// 因为Session跨域访问问题，因此采用Cookie获取Token的方式
		Cookie[] cookie = request.getCookies();
		String delToken = "";
		for (Cookie it : cookie) {
			if (it.getName().equals("Token")) {
				delToken = it.getValue();
				break;
			}
		}
		// 清除服务器后台中存储的有效token数据
		TokenSet.TOKEN_SET.remove(delToken);
		// 在服务器端获取用该Token注册过的app信息,并调用每个app的登出uri
		List<AppRegisterInfo> appRegisterList = RegisterMap.REGISTER_MAP.get(delToken);
		for (AppRegisterInfo info : appRegisterList) {
			String appLogOutUri = info.getLogOutUrl();
			String appSessionId = info.getsessionId();
			URL url = new URL(appLogOutUri);
			HttpURLConnection connection = (HttpURLConnection) url.openConnection();
			connection.setRequestMethod("POST");
			// 模拟浏览器发送请求，在Request域中设置Cookies属性
			connection.addRequestProperty("Cookie", "JSESSIONID=" + appSessionId);
			// 发送请求
			connection.connect();
			connection.disconnect();

		}
		// 清空注册的信息
		RegisterMap.REGISTER_MAP.remove(delToken);
		return "logout";
	}
}